# Form Backend Build Brief — enquiry capture, database & WhatsApp notification

**BTC-CS2026-OW-DS-EB-0001-FormBackendBuildBrief_A1-C01** · design/build team → Engineering · 13 June 2026
**Companion to:** Themis **SP-0001** (the *legal* requirements for the form backend) and **WD-0001 D-3** (the Privacy Policy copy that names the providers).
**Scope:** the enquiry form on the CyberSight Forensics marketing site — capture → **store in a database** → **notify the team via WhatsApp**. Engineering owns the technical design; this brief sets the target architecture and **maps every choice back to the SP-0001 blockers** so the build clears the Go-Live Gate first time.

> **One rule above all (SP-1):** the form must not be able to store or send anything until the **Privacy Policy is live and linked**. Build the capture path disabled-by-default and gate it on that.

---

## 1 · What the form collects

From the rendered form (`#intake-form`):

| Field | id | Type | Notes |
|---|---|---|---|
| Name | `f-name` | text, required | personal data |
| Organisation | `f-org` | text, required | |
| Work email | `f-email` | email, required | personal data |
| Phone | `f-phone` | tel, optional | personal data |
| Interested in | `f-interest` | select | pilot / imaging / retainer / other |
| Message | `f-msg` | textarea | free text — **may contain anything**; treat as sensitive (SP-7) |

Treat **all** of it as personal data. No accounts, no analytics, no other capture anywhere on the site (WD-0001 D-4).

---

## 2 · Target architecture

```
  Browser (static site, no trackers)
        │  HTTPS POST  (TLS 1.2+)
        ▼
  ┌─────────────────────────────────────────────┐
  │  Backend endpoint  /api/enquiry              │   ← runs in a UK/EEA region
  │  • origin/CSRF check, rate-limit, honeypot   │
  │  • server-side validation + sanitisation     │
  │  • strip PII from all logs (SP-7)            │
  └───────────────┬───────────────┬─────────────┘
                  │               │
   write (encrypted at rest)      │  trigger only — NO PII
                  ▼               ▼
        ┌──────────────────┐   ┌────────────────────────────┐
        │  Database (UK/EEA)│   │  WhatsApp Business API      │
        │  enquiries table  │   │  → PII-FREE alert to staff  │
        │  encrypted, RBAC  │   │  "New enquiry — open console"│
        └────────┬─────────┘   └────────────────────────────┘
                 │
        Admin console (authenticated, least-privilege)
        — staff read the actual enquiry here, never in WhatsApp
                 │
        Scheduled job: delete enquiries > retention period
```

**The single most important design decision is the WhatsApp message carries no personal data** (see §4). That keeps all enquiry PII inside the UK/EEA database and keeps Meta *out* of the enquiry-data processing chain — which is what makes the residency story (SP-2) clean.

---

## 3 · Database

- **Region:** UK or EEA only (SP-2). e.g. a managed Postgres/MySQL in London/Dublin/Frankfurt; or a UK/EEA-pinned managed store. Confirm region in writing from the provider.
- **Encryption:** at rest (provider-managed KMS is fine) **and** in transit between app and DB (SP-4).
- **Access:** least-privilege (SP-5) — only roles that handle enquiries; authenticated; access logged. No shared logins.
- **Suggested schema:**

| Column | Type | Notes |
|---|---|---|
| `id` | uuid PK | also used as the non-identifying reference in the WhatsApp alert |
| `created_at` | timestamptz | drives retention |
| `name` | text | |
| `organisation` | text | |
| `email` | text | |
| `phone` | text, null | |
| `interest` | text | enum-ish |
| `message` | text | |
| `status` | text | `new` / `actioned` / `closed` (helps the access/retention routine) |
| `source_ip_hash` | text, null | **hashed**, spam triage only — not raw IP; short-lived |

- **Retention (SP-6):** a **scheduled job** (not a human) deletes rows where `created_at` is older than the retention period **and** `status` is not an active engagement — default **12 months from last contact** (confirm via Facts Pack C4). Demonstrate the deletion routine in staging before launch.

---

## 4 · WhatsApp notification — **PII-free by design**

**Goal:** alert the CyberSight team the moment an enquiry lands, without sending any enquirer personal data to WhatsApp/Meta.

**Why PII-free.** WhatsApp Business messaging is operated by Meta and routes through Meta infrastructure (outside the UK/EEA). If the alert contained the enquirer's name, email or message, Meta would become a processor of enquiry personal data — triggering an international-transfer mechanism (IDTA/SCCs), an Art 28 contract, and a new entry in the Privacy Policy's "who else is involved" list. **Sending a content-free trigger avoids all of that.**

**Design:**
- The recipients are **CyberSight's own staff numbers** (internal alert), *not* the enquirer. The enquirer is never contacted on WhatsApp.
- The message contains **no enquiry personal data** — only a generic prompt + a non-identifying reference (the row `id`) + a timestamp.
- Staff open the **authenticated admin console** to read the actual enquiry. The console is where PII lives.

**Approved-template example** (business-initiated messages need a pre-registered template):

> 🔔 *New website enquiry received ({{1}}). Open the CyberSight console to view and respond. Ref {{2}}.*
> — where `{{1}}` = timestamp, `{{2}}` = enquiry id. **No name, email, phone or message text.**

**Provider options (pick one; record residency + Art 28 either way):**
- **Meta WhatsApp Business Cloud API** (direct) — simplest; note Meta as the messaging processor for the *number + generic template only*.
- **A BSP** (e.g. Twilio, Vonage, 360dialog, Bird) — check the BSP's data location; an EU-hosted BSP is preferable.

**Failure handling:** the WhatsApp send is **best-effort and decoupled** from capture — if WhatsApp is down, the enquiry must still store successfully and the team picks it up in the console. Never block or fail the enquiry on a notification error. Queue/retry the alert; never put PII into the retry payload or logs.

**Principal action (Facts Pack C3):** confirm the staff recipient number(s) and approve this PII-free design.

---

## 5 · Security & anti-spam (SP-4, SP-8 + site-audit items)

- **TLS** enforced site-wide; HTTP→HTTPS redirect; HSTS (`max-age=31536000; includeSubDomains; preload`).
- **CSRF** protection on the POST; same-origin/origin check.
- **Rate limiting** per IP + global; **honeypot** field already present in the markup (hidden inputs) — reject on fill; optional invisible captcha if spam persists (must not load third-party trackers on the page — SP-8).
- **Server-side validation** of every field (length caps, email format, strip control chars). Never trust client validation.
- **No third-party calls from the form page** (SP-8): no analytics, ads, social SDKs. Network tab on the form page = first-party + your `/api/enquiry` endpoint only.
- **Logging hygiene (SP-7):** scrub field values from access/error/diagnostic logs; if special-category or third-party data arrives in `message`, the process is to restrict access and delete where not needed — do not propagate it into logs or downstream tools.
- **Security headers** (also in the site audit): CSP, `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Permissions-Policy: camera=(), microphone=(), geolocation=()`.

---

## 6 · Processor contracts & residency (SP-2, SP-3)

Before a provider is used, have on file:
- **Form/email + hosting + database + WhatsApp provider:** each engaged under a **written Art 28 processor contract**, and a **data-residency attestation** (UK/EEA). If any processes outside the UK, an approved transfer mechanism (UK **IDTA** or EU SCCs + UK Addendum) must be in place first.
- The chosen **form/email provider** and **hosting provider** names fill the brackets in **WD-0001 D-3** (Privacy Policy). Because the WhatsApp alert is PII-free, Meta/BSP does **not** need to be listed as an enquiry-data processor — but confirm this position with Themis when you report the stack.

---

## 7 · API contract (suggested)

`POST /api/enquiry` — `Content-Type: application/json`
```json
{ "name": "", "organisation": "", "email": "", "phone": "", "interest": "pilot|imaging|retainer|other", "message": "", "_hp": "" }
```
- `_hp` = honeypot, must be empty.
- **200** `{ "ok": true, "ref": "<uuid>" }` → show the existing in-page success state (`#form-ok`, `role="status"`).
- **400** validation → inline field errors. **429** rate-limited. **5xx** → generic "try again / email us" message; never echo internals.

---

## 8 · SP-0001 compliance map (what Engineering reports back)

| SP-0001 | Requirement | Met by |
|---|---|---|
| SP-1 **[BLOCKER]** | No capture before privacy notice live | Capture gated/disabled until `/privacy` resolves |
| SP-2 **[BLOCKER]** | UK/EEA residency | DB + app in UK/EEA region; **WhatsApp alert PII-free** (§4) |
| SP-3 **[BLOCKER]** | Art 28 processor contracts | Signed terms for form/email, hosting, DB, WhatsApp provider (§6) |
| SP-4 | Encryption in transit + at rest | TLS site-wide; DB at-rest encryption (§3, §5) |
| SP-5 | Least-privilege access, logged | Console RBAC + access log (§3) |
| SP-6 | Retention + deletion routine | Scheduled deletion job, demoed in staging (§3) |
| SP-7 | Sensitive-content handling | Log scrubbing + restrict/delete process (§5) |
| SP-8 | No silent third-party calls | First-party-only form page (§5) |

**Report to Themis for the Go-Live Gate:** (1) chosen form/email + hosting providers with residency attestation + Art 28 terms; (2) TLS + at-rest encryption confirmed; (3) the retention/deletion routine + who has access; (4) confirmation the privacy notice is live before capture is enabled; (5) the WhatsApp provider + confirmation the alert is PII-free.

---

*Prepared by the design/build team, 13 June 2026. Legal requirements owned by Themis SP-0001 — this brief implements them; raise any legal-intent question to Themis via the founder before deviating.*
